import os
import time
import traceback
import uuid

from PyQt5.QtCore import pyqtSignal, QThread
from loguru import logger
from retrying import retry

from business.business import Business
from tts import func, main
from utils import model, utils


class Ffmpeg:

    @classmethod
    def play_voice(cls, voice_fuu_path):
        cmd = 'ffplay -nodisp -autoexit {}'.format(voice_fuu_path)
        logger.info(cmd)
        utils.get_execute_cmd_result(cmd, True)

    @classmethod
    def generate_all_audio(cls, audio_list: list, output_path: str):
        audio_list_str = '|'.join(audio_list)
        cmd = 'ffmpeg -y -i "concat:{}" -acodec copy {}'.format(audio_list_str, output_path)
        logger.info(cmd)
        utils.get_execute_cmd_result(cmd, True)


class RoleWork(QThread):
    count = int(0)
    count_signal = pyqtSignal()
    tts_error = pyqtSignal()

    def __init__(self, count, parent=None):
        super(RoleWork, self).__init__(parent)
        self.count = count
        self.business = Business()
        self.play_flag = True

    def run(self):
        try:
            tts_full_path = self.business.get_tts_path_uuid(self.count)
            if os.path.exists(tts_full_path):
                logger.info('count={} path={} already exist, skip'.format(self.count, tts_full_path))
            else:
                role_config = self.business.role_config_model[self.business.text_dict[self.count].role]
                role_config = role_config.dict()
                text = {'text': self.business.text_dict[self.count].text}
                voice_config = {**role_config, **text}
                voice_config = model.VoiceModel(**voice_config)
                xml = func.generate_xml_str(voice_config)
                logger.info('count={} xml={}'.format(self.count, xml))
                main.start(xml, tts_full_path)
            if self.play_flag:
                Ffmpeg.play_voice(tts_full_path)
            self.play_flag = True
        except Exception as e:
            self.tts_error.emit()
            logger.error('tts error={}'.format(str(e)))
            logger.error(traceback.format_exc())
        finally:
            self.count_signal.emit()


class VoiceWork(QThread):
    count_signal = pyqtSignal()

    def __init__(self, parent=None):
        super(VoiceWork, self).__init__(parent)
        self.voice_model: model.VoiceModel = None
        self.business = Business()
        self.auto_play_flag = True
        self.complete_open_flag = False

    def run(self):
        try:
            tts_uuid_name = self.business.get_tts_uuid_name(dict(self.voice_model))
            tts_full_path = self.business.get_abs_tts_full_path(tts_uuid_name)
            if os.path.exists(tts_full_path):
                logger.info('path={} already exist, skip'.format(tts_full_path))
            else:
                xml = func.generate_xml_str(self.voice_model)
                logger.info('voice work xml={}'.format(xml))
                main.start(xml, tts_full_path)
            if self.complete_open_flag:
                os.system(f'explorer /select, {tts_full_path}')
            if self.auto_play_flag:
                Ffmpeg.play_voice(tts_full_path)
        except Exception as e:
            logger.error('tts error={}'.format(str(e)))
            logger.error(traceback.format_exc())
        finally:
            self.count_signal.emit()


class AllTextWork(QThread):
    count_signal = pyqtSignal()
    slider = pyqtSignal(int)
    play_all_failed = pyqtSignal()

    def __init__(self, parent=None):
        super(AllTextWork, self).__init__(parent)
        self.business = Business()
        self.auto_play_flag = True
        self.complete_open_flag = False

    @retry(wait_random_min=100, wait_random_max=3000, stop_max_attempt_number=3)
    def wait_all_tts_file_exist(self):
        def send_percent(value: dict):
            true = len([i for i in value.values() if i])
            percent = int(true / len(value.values()) * 100)
            print()
            self.slider.emit(percent)

        tts_file_list = [self.business.get_tts_path_uuid(i) for i in range(len(self.business.item_list))]
        tts_base_name_list = [file.split('\\')[-1] for file in tts_file_list]
        logger.info('need play tts file name={}'.format(tts_base_name_list))
        result = {file: False for file in tts_base_name_list}
        start_time = time.perf_counter()
        while True:
            for file in tts_base_name_list:
                if not result[file]:
                    if file in os.listdir(self.business.output_dir):
                        result[file] = True
            send_percent(result)
            if all(result.values()):
                logger.info('all tts file exist, result={}'.format(result))
                break
            if time.perf_counter() - start_time > 60:
                raise Exception('timeout, some tts thread may stack')
            time.sleep(0.5)
        return tts_file_list, tts_base_name_list

    def run(self):
        try:

            for item in self.business.item_list:
                item.worker.play_flag = False
                item.play_music()
            try:
                tts_file_list, tts_base_name_list = self.wait_all_tts_file_exist()
            except Exception as e:
                logger.error('tts failed, error={}'.format(str(e)))
                self.play_all_failed.emit()
            all_text_file_uuid = uuid.uuid3(uuid.NAMESPACE_DNS, ''.join(tts_base_name_list))
            logger.info('all text file uuid is {}'.format(all_text_file_uuid))
            all_text_file_path = os.path.abspath(os.path.join(
                self.business.output_dir, "{}.mp3".format(all_text_file_uuid)))
            if os.path.exists(all_text_file_path):
                logger.info('generate all tts file exist={}'.format(all_text_file_path))
            else:
                Ffmpeg.generate_all_audio(tts_file_list, all_text_file_path)
            if self.complete_open_flag:
                os.system(f'explorer /select, {all_text_file_path}')
            if self.auto_play_flag:
                Ffmpeg.play_voice(all_text_file_path)
        except Exception as e:
            logger.error('tts error={}'.format(str(e)))
            logger.error(traceback.format_exc())
        finally:
            self.count_signal.emit()
